/**
  ******************************************************************************
  * @file
  * @brief
  * @author taoye
  * @version V1.0.0
  * @date 2022/08/01
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "A_memory.h"
#include "A_memory_shell.h"
/* Private defines -----------------------------------------------------------*/

#define     FLASH_DEFAULT_VAL           (void *)0xFFFFFFFF

#define condition_traverse(list_head, struct_type, struct_p, condition)         \
    list_node = NULL;                                                           \
    slist_for_each(list_node, list_head){                                       \
    	struct_p = slist_entry(list_node, struct_type, list);                   \
    	condition{                                                              \
    		break;                                                              \
    	}                                                                       \
    }                                                                           \

#define     ALIGN_BYTE(byte, size)          (((size) + (byte) - 1) & ~((byte) - 1))   
#define     HARD_ALIGN_BYTE(size)           ALIGN_BYTE(hard->align, size)

#define     GET_AREA_HARD(ret_value)                            \
    A_mem_hard_item_t * hard = Find_Hard(area->hard_name);            \
    if(hard == NULL){                                           \
        return ret_value;                                       \
    }                                                           \
    PARA_ASSERT(hard->Read);                                    \
    PARA_ASSERT(hard->Write);                                   \

#define     PARA_ASSERT(x)                  \
    if(x == NULL){                          \
        while(1);                           \
    }                                       \

/* Public enum    -----------------------------------------------------------*/

/* Private Struct    ---------------------------------------------------------------*/

/* Private Variable  ---------------------------------------------------------------*/
static slist_t hard_item_list =
{
    .next = NULL,
};
static slist_t area_item_list =
{
    .next = NULL,
};

/***********************************************************************************
 * @brief
 * @ex:         获取硬件注册链表
 * @par
 * @retval
 **********************************************************************************/
slist_t *Get_Hard_list(void)
{
    return &hard_item_list;
}

/***********************************************************************************
 * @brief
 * @ex:         获取区域注册链表
 * @par
 * @retval
 **********************************************************************************/
slist_t *Get_Area_list(void)
{
    return &area_item_list;
}

/***********************************************************************************
 * @brief       
 * @ex:         根据区域名找到区域链表项
 * @par
 * @retval   	success:0
 * 				failed:-1
 **********************************************************************************/
A_mem_area_item_t * Find_Area(uint32_t area_name)
{
    slist_t     *   list_node   = NULL;
    A_mem_area_item_t *   area;

    condition_traverse(&area_item_list, A_mem_area_item_t, area,
    if(area->name == area_name));

    if(list_node != NULL){
        return area;
    }

    return NULL;
}
/***********************************************************************************
 * @brief       
 * @ex:         根据硬件名找到硬件链表项
 * @par
 * @retval   	success:0
 * 				failed:-1
 **********************************************************************************/
A_mem_hard_item_t * Find_Hard(uint32_t hard_name)
{
    slist_t     *   list_node   = NULL;
    A_mem_hard_item_t *   hard;

    condition_traverse(&hard_item_list, A_mem_hard_item_t, hard,
    if(hard->name == hard_name));

    if(list_node != NULL){
        return hard;
    }

    return NULL;    
}

/***********************************************************************************
 * @brief    功能说明：注册Flash 分区基地址、大小、地址对齐字节数、读写擦除控制函数
 * @ex:
* @par      输入参数：const char *    hard_name - 分区名称
 *                     uint32_t        base_addr - 分区基地址
 *                     size_t          size - 分区大小
 *                     uint8_t         align - 分区地址对齐字节数
 *                     Amem_Write      Write - 分区写函数地址
 *                     Amem_Read       Read  - 分区读函数地址  
 *                     Amem_Erase      Erase - 分区擦除函数地址
 * @retval   	success:0
 * 				failed:-1
 **********************************************************************************/
int Amem_Hardware_Register
(
    const char *    hard_name,
    uint32_t        base_addr,
    size_t          size,
    uint8_t         align,
    Amem_Write      Write, 
    Amem_Read       Read,  
    Amem_Erase      Erase )
{
    if(base_addr != (ALIGN_BYTE(align, base_addr))){
        return -1;
    }

    A_mem_hard_item_t * hard = (A_mem_hard_item_t *)Ram_Malloc(sizeof(A_mem_hard_item_t));
    if(hard == NULL){
        return -1;
    }

    hard->st        = Ram_Malloc(strlen(hard_name) + 1);
    strcpy(hard->st, hard_name);
    hard->name      = String_To_Crc32(hard_name);
    hard->base_addr = base_addr;
    hard->used      = 0;
    hard->size      = size;
    hard->align     = align;
    hard->Write     = Write;
    hard->Read      = Read;
    hard->Erase     = Erase;

    slist_append(&hard_item_list, &hard->list);

    return 0;
}

/***********************************************************************************
 * @brief    功能说明：新建Flash存储区域分区初始化
 * @ex:         
* @par      输入参数：const char *    area_name - 分区名称，寻址用
*                    const char *    hard_name - 对应MCU Flash 或 IAP Flash存储区名称
 *                   uint32_t        offset_addr - 分区偏移地址
 *                   size_t          size  - 分区大小，单位字节
 * @retval   	success:0
 * 				failed:-1
 **********************************************************************************/
int Amem_Area_Register
(
    const char *    area_name,
    const char *    hard_name,
    uint32_t        offset_addr,
    size_t          size )
{
    A_mem_area_item_t *   area;
    area = (A_mem_area_item_t *)Ram_Malloc(sizeof(A_mem_area_item_t));
    if(area == NULL){
        return -1;
    }

    area->st        = Ram_Malloc(strlen(area_name) + 1);
    strcpy(area->st, area_name);
    area->name      = String_To_Crc32(area_name);
    area->hard_name = String_To_Crc32(hard_name);

    A_mem_hard_item_t * hard = Find_Hard(area->hard_name);    
    if(hard == NULL){
        Ram_Free(area);                                         
        return -1;                                      
    }                                                                                               

    // 未4字节对齐
    if(size != (HARD_ALIGN_BYTE(size))){
        Ram_Free(area);
        return -1;
    }

    area->offset_addr     = offset_addr;
    area->size          = size;

    slist_append(&area_item_list, &area->list);

    return 0;
}
/***********************************************************************************
 * @brief
 * @ex:
 * @par
 * @retval   	success:0
 * 				failed:-1
 **********************************************************************************/
int Amem_Check_Hardware
(
    Amem_nametype_e nametype,
    void        *   hard_name )
{
    // 字符转校验码
    if(nametype == AMEM_String){
        hard_name = (void *)String_To_Crc32((char *)hard_name);
    }

    if(Find_Hard((uint32_t)hard_name) == NULL){
        return -1;
    }

    return 0;
}
/***********************************************************************************
 * @brief
 * @ex:
 * @par
 * @retval   	success:0
 * 				failed:-1
 **********************************************************************************/
int Amem_Check_Area
(
    Amem_nametype_e nametype,
    void        *   area_name )
{
    // 字符转校验码
    if(nametype == AMEM_String){
        area_name = (void *)String_To_Crc32((char *)area_name);
    }

    if(Find_Area((uint32_t)area_name) == NULL){
        return -1;
    }

    return 0;
}
/***********************************************************************************
 * @brief
 * @ex:
 * @par
 * @retval   	align
 **********************************************************************************/
uint8_t Amem_Get_AreaAlign
(
    Amem_nametype_e nametype,
    void        *   area_name )
{
    // 字符转校验码
    if(nametype == AMEM_String){
        area_name = (void *)String_To_Crc32((char *)area_name);
    }

    A_mem_area_item_t *area = Find_Area((uint32_t)area_name);
    if(area == NULL){
        return 0;
    }

    A_mem_hard_item_t *hard = Find_Hard(area->hard_name);
    if(hard == NULL){
        return 0;
    }

    return hard->align;
}
/***********************************************************************************
 * @brief
 * @ex:
 * @par
 * @retval   	size
 **********************************************************************************/
size_t Amem_Get_AreaSize
(
    Amem_nametype_e nametype,
    void        *   area_name )
{
    // 字符转校验码
    if(nametype == AMEM_String){
        area_name = (void *)String_To_Crc32((char *)area_name);
    }

    A_mem_area_item_t *area = Find_Area((uint32_t)area_name);
    if(area == NULL){
        return 0;
    }

    return area->size;    
}
/***********************************************************************************
 * @brief
 * @ex:
 * @par
 * @retval   	Erase
 **********************************************************************************/
Amem_Erase Amem_Get_AreaErase
(
    Amem_nametype_e nametype,
    void        *   area_name )
{
    // 字符转校验码
    if(nametype == AMEM_String){
        area_name = (void *)String_To_Crc32((char *)area_name);
    }

    A_mem_area_item_t *area = Find_Area((uint32_t)area_name);
    if(area == NULL){
        return 0;
    }

    A_mem_hard_item_t *hard = Find_Hard(area->hard_name);
    if(hard == NULL){
        return 0;
    }

    return hard->Erase;
}
/***********************************************************************************
 * @brief
 * @ex:         
 * @par
 * @retval   	success:0
 * 				failed:-1
 **********************************************************************************/
int Amem_Area_Write
(
    Amem_nametype_e  nametype,
    void        *   area_name,
    uint8_t     *   data,
    uint32_t        offset_addr,
    size_t          size )
{
    // 字符转校验码
    if(nametype == AMEM_String){
        area_name = (void *)String_To_Crc32((char *)area_name);
    }

    A_mem_area_item_t *area = Find_Area((uint32_t)area_name);
    if(area == NULL){
        return -1;
    }

    GET_AREA_HARD(-1);

    uint32_t addr = hard->base_addr + area->offset_addr + offset_addr;

    if(addr != HARD_ALIGN_BYTE(addr)){
        return -1;
    }
    if(size != HARD_ALIGN_BYTE(size)){
        return -1;
    }

    (hard->Write)(addr, data, size);

    return 0;
}
/***********************************************************************************
 * @brief
 * @ex:         
 * @par
 * @retval   	success:0
 * 				failed:-1
 **********************************************************************************/
int Amem_Area_Read
(
    Amem_nametype_e  nametype,
    void        *   area_name,
    uint8_t     *   data,
    uint32_t        offset_addr,
    size_t          size )
{
    // 字符转校验码
    if(nametype == AMEM_String){
        area_name = (void *)String_To_Crc32((char *)area_name);
    }

    A_mem_area_item_t *area = Find_Area((uint32_t)area_name);
    if(area == NULL){
        return -1;
    }

    GET_AREA_HARD(-1);

    uint32_t addr = hard->base_addr + area->offset_addr + offset_addr;

    if(addr != HARD_ALIGN_BYTE(addr)){
        return -1;
    }
    if(size != HARD_ALIGN_BYTE(size)){
        return -1;
    }

    (hard->Read)(addr, data, size);

    return 0;    
}
/***********************************************************************************
 * @brief
 * @ex:         
 * @par
 * @retval   	success:0
 * 				failed:-1
 **********************************************************************************/
int Amem_Area_FullErase
(
    Amem_nametype_e  nametype,
    void        *   area_name )
{
    // 字符转校验码
    if(nametype == AMEM_String){
        area_name = (void *)String_To_Crc32((char *)area_name);
    }

    A_mem_area_item_t *area = Find_Area((uint32_t)area_name);
    if(area == NULL){
        return -1;
    }

    GET_AREA_HARD(-1);

    uint32_t addr = hard->base_addr + area->offset_addr;

	if((hard->Erase) != NULL){
		(hard->Erase)(addr, area->size);
	}
	else{//没有擦除接口，则以写代替擦
		uint32_t total_count = area->size/hard->align;
		uint8_t * defalut_data = (uint8_t*)Ram_Malloc(hard->align);
		
		if(defalut_data == NULL)
			return -1;
		
		memset(defalut_data, 0xFF, hard->align);
		
		for(uint32_t i = 0; i < total_count; i++){
			(hard->Write)(addr+(hard->align)*i, defalut_data, hard->align);
		}
		
		Ram_Free(defalut_data);
	}

    return 0;
}
/***********************************************************************************
 * @brief
 * @ex:         
 * @par
 * @retval   	success:0
 * 				failed:-1
 **********************************************************************************/
int Amem_Area_Erase
(
    Amem_nametype_e nametype,
    void        *   area_name,
    uint32_t        offset_addr,
    size_t          size )
{
    // 字符转校验码
    if(nametype == AMEM_String){
        area_name = (void *)String_To_Crc32((char *)area_name);
    }

    A_mem_area_item_t *area = Find_Area((uint32_t)area_name);
    if(area == NULL){
        return -1;
    }

    GET_AREA_HARD(-1);

    uint32_t addr = hard->base_addr + area->offset_addr;

	if((hard->Erase) != NULL){
		(hard->Erase)(addr + offset_addr, size);
	}
	else{//没有擦除接口，则以写代替擦
		uint32_t total_count = area->size/hard->align;
		uint8_t * defalut_data = (uint8_t*)Ram_Malloc(hard->align);
		
		if(defalut_data == NULL)
			return -1;
		
		memset(defalut_data, 0xFF, hard->align);
		
        taskENTER_CRITICAL();
		for(uint32_t i = 0; i < total_count; i++){
			(hard->Write)(addr+hard->align, defalut_data, hard->align);
		}
		taskEXIT_CRITICAL();
        
		Ram_Free(defalut_data);
	}
    return 0;    
}


